﻿#pragma once
#include "io/InputStream.h"
#include "array/Array.hpp"
#include <optional>

namespace yzrilyzr_util{
	EBCLASS(Util){
	public:
	static void readFully(yzrilyzr_io::InputStream & in, yzrilyzr_array::ByteArray & b);
	static void readFully(yzrilyzr_io::InputStream & in, yzrilyzr_array::ByteArray & b, u_index off, u_index len);
	static int32_t readInt(yzrilyzr_io::InputStream & in);
	static yzrilyzr_array::ByteArray readByteArray(yzrilyzr_io::InputStream & is, u_index lengthLimit, u_index * outLength);
	static yzrilyzr_array::ByteArray read(yzrilyzr_io::InputStream & is);
	static int32_t readVarintFromStream(yzrilyzr_io::InputStream & in);
	template<typename T>
	static T clamp(T x, T min, T max){
		return x > max?max:(x < min?min:x);
	}
	static double clamp01(double x);
	static float clamp01(float x);
	static double lerp(double startY, double endY, double x);
	static int NChoose(int n, int k);
	static double sigmoid(double i, double minV, double maxV, double ampL, double ampR);
	static double linearMap(double xStart, double xEnd, double yStart, double yEnd, double x);
	static void checkSize(u_index size);
	static void checkIndex(u_index i, u_index length);
	static void checkRange(u_index arrayLength, u_index fromIndex, u_index toIndex);
	static yzrilyzr_array::ByteArray concat(const yzrilyzr_array::ByteArray & a, const yzrilyzr_array::ByteArray & b);

	template<typename T>
	static int kmp(T * src, u_index srcLength, T * pat, u_index patLength){
		int * next=kmp_next(pat, patLength);
		int ps=0;
		int pp=0;
		while(ps < srcLength){
			if(src[ps] == pat[pp]){//模式匹配，增加index
				ps++;
				pp++;
			} else if(pp > 0) pp=next[pp - 1];//不匹配，且模式index已移动，模式index取next
			else ps++;//模式index为0，移动主index
			if(pp == patLength){
				delete next;
				return ps - pp;//模式结束，完全匹配
			}
		}
		delete next;
		return -1;
	}
	template<typename T>
	static int * kmp_next(T * pat, u_index patLength){
		int * next=new int[patLength]();//同样长度next
		int prefixLen=0;
		int i=1;//跳过第一个
		while(i < patLength){
			if(pat[prefixLen] == pat[i]) next[i++]=++prefixLen;
			else{
				if(prefixLen == 0) next[i++]=0;
				else prefixLen=next[prefixLen - 1];
			}
		}
		return next;
	}
	static uint32_t floatToIntBits(float value){
		uint32_t result;
		std::memcpy(&result, &value, sizeof(value));
		return result;
	}
	static uint64_t doubleToLongBits(double value){
		uint64_t result;
		std::memcpy(&result, &value, sizeof(value));
		return result;
	}
	static yzrilyzr_lang::String readLine(yzrilyzr_io::InputStream & is);
	static yzrilyzr_array::ByteArray b64(const yzrilyzr_lang::String & str);
	static yzrilyzr_lang::String b64(const yzrilyzr_array::ByteArray & str);
	};
}